package com.thekernel.youdaoyun.service.impl;

import com.thekernel.youdaoyun.bean.Note;
import com.thekernel.youdaoyun.bean.Star;
import com.thekernel.youdaoyun.exception.MyException;
import com.thekernel.youdaoyun.exception.impl.DataNotFoundException;
import com.thekernel.youdaoyun.exception.impl.IllegalUserException;
import com.thekernel.youdaoyun.exception.impl.SystemErrorException;
import com.thekernel.youdaoyun.exception.impl.UserNotLoginException;
import com.thekernel.youdaoyun.mapper.NoteMapper;
import com.thekernel.youdaoyun.mapper.StarMapper;
import com.thekernel.youdaoyun.service.NoteService;
import com.thekernel.youdaoyun.util.FileUtils;
import com.thekernel.youdaoyun.util.UserUtils;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.converter.WordToHtmlConverter;
import org.apache.poi.hwpf.usermodel.Picture;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.util.Date;
import java.util.List;

/**
 * @Author TheKernel
 * @Date 2019/9/14 11:51 上午
 * @Version 1.0
 * @Description NoteService 的具体实现
 */
@Service("NoteService")
public class NoteServiceImpl implements NoteService {

    @Autowired
    private NoteMapper noteMapper;

    @Autowired
    private StarMapper starMapper;

    /**
     * 添加笔记
     *
     * @param note 笔记信息(note_title, note_content, note_user)
     * @return -1: Data Not Found 0: Note Already Exists 1: Success
     */
    @Override
    public int addNote(Note note, String content) throws MyException {
        if (note.getNoteTitle() == null || note.getNoteUser() == 0) {
            return -1;
        }
        // 写入文件并返回文件名
        String filename = FileUtils.createFile(
                content
        );
        // 生成 note
        note.setNoteContent(filename);
        note.setStatus(Boolean.TRUE);
        note.setCreateTime(new Date());
        return this.noteMapper.add(note);
    }

    /**
     * 查看有效的笔记
     *
     * @param userId 用户id
     * @return 有效的笔记列表
     */
    @Override
    public Note[] queryValidNotes(Long userId) {
        return this.noteMapper.queryAllValidNotesByUserId(userId);
    }

    /**
     * 查看已进入回收站的笔记
     *
     * @param userId 用户id
     * @return 回收站的笔记列表
     */
    @Override
    public Note[] queryDeletedNotes(Long userId) {
        return this.noteMapper.queryAllDeletedNotesByUserId(userId);
    }

    /**
     * 移动笔记到回收站(逻辑删除)
     *
     * @param noteId 笔记 id
     * @return 1: 删除成功 0: 删除失败
     * @throws MyException 非本人操作等
     */
    @Override
    public int deleteNote(int noteId) throws MyException {
        if (checkValidNoteNotExists(noteId)) {
            throw new DataNotFoundException();
        }
        if (checkNoteBelong(noteId)) {
            if (this.noteMapper.queryNoteByNoteId(noteId).getFolder()) {
                Note[] notes = this.noteMapper.queryNotesBelongFolder(noteId);
                for (Note note : notes) {
                    deleteNote((int) note.getNoteId());
                }
            }
            Star star = this.starMapper.queryStarIdByNoteId(noteId);
            if (star != null) {
                this.starMapper.cancelStarByNoteId(noteId);
            }
            return this.noteMapper.deleteNoteByNoteId(noteId);
        }
        throw new IllegalUserException();
    }

    /**
     * 恢复移动到回收站的笔记
     *
     * @param noteId 笔记 id
     * @return 1: 恢复成功 0: 恢复失败
     * @throws MyException 非本人操作等
     */
    @Override
    public int recoverDeletedNote(long noteId) throws MyException {
        if (checkDeletedNoteNotExists(noteId)) {
            throw new DataNotFoundException();
        }
        if (checkNoteBelong(noteId)) {
            if (this.noteMapper.queryNoteByNoteId(noteId).getFolder()) {
                Note[] notes = this.noteMapper.queryDeletedNotesBelongFolder(noteId);
                for (Note note : notes) {
                    recoverDeletedNote(note.getNoteId());
                }
            }
            return this.noteMapper.recoverDeletedNotes(noteId);
        }
        throw new IllegalUserException();
    }

    /**
     * 删除回收站中的笔记(物理删除)
     *
     * @param noteId 笔记id
     * @return 1: 删除成功 0: 删除失败
     * @throws MyException 非本人操作等
     */
    @Override
    public int deleteDeletedNote(long noteId) throws MyException {
        if (checkDeletedNoteNotExists(noteId)) {
            throw new DataNotFoundException();
        }
        if (!checkNoteBelong(noteId)) {
            throw new IllegalUserException();
        }
        Note note = this.noteMapper.queryNoteByNoteId(noteId);
        // 删除文件
        FileUtils.deleteFile(
                note.getNoteContent()
        );
        // 删除数据库纪录
        return this.noteMapper.deleteDeletedNoteByNoteId(noteId);
    }

    /**
     * 根据 Note id 查询 Note
     *
     * @param noteId 笔记 id
     * @return 笔记内容
     * @throws MyException 非本人操作等
     */
    @Override
    public String queryNote(long noteId) throws MyException {
//        if (!checkNoteBelong(noteId)) {
//            throw new IllegalUserException();
//        }
        Note note = this.noteMapper.queryNoteByNoteId(noteId);
        return FileUtils.readFile(note.getNoteContent());
    }

    /**
     * 更新笔记(笔记密码等)
     *
     * @param note 笔记信息()
     * @return 1: 更新成功 0: 笔记不存在
     * @throws MyException 非本人操作等
     */
    @Override
    public int updateNote(Note note) throws MyException {
        checkNote(note);
        // 数据库更新
        return this.noteMapper.updateNote(note);
    }

    /**
     * 重命名笔记
     *
     * @param note 新的 note_title 以及 note_id
     * @return 1: 更新成功 0: 更新失败
     * @throws MyException 非本人操作等
     */
    @Override
    public int renameNote(Note note) throws MyException {
        checkNote(note);
        if (note.getNoteId() == 0 || note.getNoteTitle() == null) {
            throw new DataNotFoundException();
        }
        return this.noteMapper.renameNote(note);
    }

    /**
     * 更新笔记内容
     *
     * @param note    笔记
     * @param content 新笔记内容
     */
    @Override
    public void updateNoteContent(Note note, String content) throws MyException {
        Note dbNote = this.noteMapper.queryNoteByNoteId(note.getNoteId());
        // 文件更新
        FileUtils.updateFile(dbNote.getNoteContent(), content);
        // 数据库更新
        note.setCreateTime(new Date());
        if (!this.noteMapper.updateNoteContent(note)) {
            throw new SystemErrorException();
        }
    }

    /**
     * 添加文件夹
     *
     * @param note 文件夹信息
     * @return 文件夹 id
     */
    @Override
    public int addFolder(Note note) throws UserNotLoginException {
        if (note.getNoteTitle() == null) {
            return -1;
        }
        note.setNoteUser(UserUtils.checkReturnUserId());
        note.setCreateTime(new Date());
        return this.noteMapper.addFolder(note);
    }

    /**
     * 全部恢复
     *
     * @return 1: 恢复成功 0: 恢复失败
     */
    @Override
    public int recoverAllDeleted() throws UserNotLoginException {
        long userId = UserUtils.checkReturnUserId();
        return this.noteMapper.recoverAllDeleted(userId);
    }

    /**
     * 清空回收站
     *
     * @return 1: 清空成功 0: 清空失败
     */
    @Override
    public int deletedAllDeleted() throws UserNotLoginException {
        long userId = UserUtils.checkReturnUserId();
        return this.noteMapper.deletedAllDeleted(userId);
    }

    /**
     * 清空回收站
     *
     * @return 1: 清空成功 0: 清空失败
     */
    @Override
    public Note[] searchNoteByKeyword(String keyword) throws UserNotLoginException {
        long userId = UserUtils.checkReturnUserId();
        return this.noteMapper.queryNotesByKeyword(keyword, userId);
    }

    /**
     * 根据 noteId 查询笔记
     *
     * @param noteId 笔记id
     * @return note
     */
    @Override
    public Note queryNoteById(int noteId) {
        return this.noteMapper.queryNoteByNoteId(noteId);
    }

    /**
     * 根据文件夹查找文件
     *
     * @param pid 文件夹id
     * @return 文件夹下的文件
     */
    @Override
    public Note[] queryNoteByPid(int pid) throws UserNotLoginException {
        long userId = UserUtils.checkReturnUserId();
        return this.noteMapper.queryNotesByCategory(pid, userId);
    }

    /**
     * 根据笔记设置对应的排序
     *
     * @param noteId 笔记id
     */
    @Override
    public void addZ(int noteId) {
        int z = this.noteMapper.maxZ() + 1;
        this.noteMapper.addZ(noteId, z);
    }

    /**
     * 从word中读取数据
     *
     * @param file word 文件
     * @return 0: 上传失败 1: 上传成功
     */
    @Override
    public String importFromWord(File file) throws IOException, ParserConfigurationException, TransformerException {
        InputStream input = new FileInputStream(file);

        //实例化WordToHtmlConverter，为图片等资源文件做准备
        WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter(
                DocumentBuilderFactory.newInstance().newDocumentBuilder()
                        .newDocument());
        wordToHtmlConverter.setPicturesManager((content, pictureType, suggestedName, widthInches, heightInches) -> suggestedName);
        // docx
        HWPFDocument wordDocument = new HWPFDocument(input);
        wordToHtmlConverter.processDocument(wordDocument);
        List pics = wordDocument.getPicturesTable().getAllPictures();
        for (Object o : pics) {
            Picture pic = (Picture) o;
            try {
                pic.writeImageContent(new FileOutputStream("data/" + pic.suggestFullFileName()));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }

        // 转换
        Document htmlDocument = wordToHtmlConverter.getDocument();
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        DOMSource domSource = new DOMSource(htmlDocument);
        StreamResult streamResult = new StreamResult(outStream);
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer serializer = tf.newTransformer();
        serializer.setOutputProperty(OutputKeys.ENCODING, "utf-8");
        serializer.setOutputProperty(OutputKeys.INDENT, "yes");
        serializer.setOutputProperty(OutputKeys.METHOD, "html");
        serializer.transform(domSource, streamResult);
        outStream.close();
        return new String(outStream.toByteArray());
    }


    /**
     * 检查 note
     *
     * @param note note 信息
     * @throws MyException 非本人操作
     */
    private void checkNote(Note note) throws MyException {
        if (note.getNoteId() == 0) {
            throw new DataNotFoundException();
        }
        if (checkValidNoteNotExists(note.getNoteId())) {
            throw new DataNotFoundException();
        }
        if (!checkNoteBelong(note.getNoteId())) {
            throw new IllegalUserException();
        }
    }

    /**
     * 查询回收站中是否存在笔记
     *
     * @param noteId 笔记 id
     * @return false: 存在 true: 不存在
     * @throws DataNotFoundException 笔记不存在
     */
    private boolean checkDeletedNoteNotExists(long noteId) throws DataNotFoundException {
        try {
            return this.noteMapper.queryDeletedNoteByNoteId(noteId) == null;
        } catch (NullPointerException e) {
            throw new DataNotFoundException();
        }
    }

    /**
     * 查询该笔记是否有效
     *
     * @param noteId 笔记 id
     * @return false: 存在 true: 不存在
     * @throws DataNotFoundException 笔记不存在
     */
    private boolean checkValidNoteNotExists(long noteId) throws DataNotFoundException {
        try {
            return this.noteMapper.queryValidNoteByNoteId(noteId) == null;
        } catch (NullPointerException e) {
            throw new DataNotFoundException();
        }
    }

    /**
     * 查询笔记归属
     *
     * @param noteId 笔记 id
     * @return true: 属于登录用户 false: 不属于登录用户
     * @throws UserNotLoginException 用户未登录
     */
    private boolean checkNoteBelong(long noteId) throws UserNotLoginException {
        long userId = UserUtils.checkReturnUserId();
        long noteUser = this.noteMapper.queryNoteUserByNoteId(noteId).getNoteUser();

        return userId == noteUser;
    }

    private String getNoteFilePath(Note note) {
        return note.getNoteUser() + File.separator +
                note.getCategory() + File.separator +
                note.getNoteTitle();
    }


}
